package com.mjk.common.core.jdbc.service.plus.idbservice.impl;

import com.github.pagehelper.Page;
import com.github.pagehelper.page.PageMethod;
import com.mjk.common.base.model.BaseEntity;
import com.mjk.common.base.model.BaseExample;
import com.mjk.common.base.model.annotation.DefaultValue;
import com.mjk.common.core.jdbc.annotation.ArcSharding;
import com.mjk.common.core.jdbc.annotation.Id;
import com.mjk.common.core.jdbc.bean.BeePage;
import com.mjk.common.core.jdbc.datasource.DynamicDataSource;
import com.mjk.common.core.jdbc.service.plus.idbservice.BeeDBService;
import com.mjk.common.core.jdbc.service.plus.mapper.WrapMapper;
import com.mjk.common.core.jdbc.service.plus.provider.BeeDataSourceType;
import com.mjk.common.core.jdbc.service.plus.wrap.BeeComplexQueryWrapper;
import com.mjk.common.core.jdbc.service.plus.wrap.BeeQueryWrapper;
import com.mjk.common.core.jdbc.util.TableUtils;
import com.mjk.common.core.jdbc.util.provider.SelectSqlProvider;
import com.mjk.common.core.jdbc.util.provider.SqlProvider;
import com.mjk.common.tools.lambada.LambdaReflectionUtil;
import com.mjk.common.tools.springextend.BeeApplicationListener;
import com.mjk.common.util.*;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class BeeDBServiceImpl<T extends BaseEntity> implements BeeDBService<T>, BeeApplicationListener {
    /**
     * 保存的mapper
     */
    private Map<String,Object> mappers;

    @Autowired
    private WrapMapper beeMapper;

    @Value("${sharding.jdbc.datasource.defaultname:his}")
    private String defaltSource;
    /**
     * 默认批量操作大小 update /save list的大小
     */
    private Integer defaultPageSize = 1000;

    public Object getMapper(BaseEntity t){
        String className = t.getClass().getName()+"Mapper";
        ArcSharding sharding = t.getClass().getAnnotation(ArcSharding.class);
        if(null!=sharding&& ObjectUtils.isNotEmpty(sharding.dataSource())){
            DynamicDataSource.setDatabaseType(sharding.dataSource());
        }else {//如果没有配置 默认为defaultname
            DynamicDataSource.setDatabaseType(defaltSource);
        }
        if(null!=mappers && mappers.containsKey(className)) {
            return mappers.get(className);
        }
        if(null!=getMapper()) {
            return getMapper();
        }
        return beeMapper;
    }

    public Object getMapper(){
        return null;
    }



    public List<T> findManyByComplexWrapper(BeeComplexQueryWrapper wrapper){
        Object o = LambdaReflectionUtil.invoke(getMapper(wrapper.getInstance()), "complexSelectByWrapp",  wrapper);
        if (o != null) {
            return (List<T>) SqlProvider.getListFromListMap(o, wrapper.getInstanceClass(),wrapper);
        }
        return new ArrayList<>();
    }

    @Override
    public BeePage<T> findManyByComplexWrapperWithPage(BeeComplexQueryWrapper warpper) {
        if (ObjectUtils.isNotEmpty(warpper.getPageSize()) && ObjectUtils.isNotEmpty(warpper.getPageStart())) {
            Page page;
            if (ObjectUtils.isNotEmpty(warpper.orderBy())) {
                page = PageMethod.startPage(warpper.getPageStart(), warpper.getPageSize(), warpper.orderBy());
            } else {
                page = PageMethod.startPage(warpper.getPageStart(), warpper.getPageSize());
            }
            LambdaReflectionUtil.invoke(getMapper(warpper.getInstance()), "complexSelectByWrappWithPage", warpper);
            if (page != null) {
                return new BeePage<>(page, warpper);
            }
            return new BeePage(new ArrayList<T>());
        } else {
            return new BeePage(findManyByComplexWrapper(warpper));
        }
    }

    @Override
    public Integer countByWrapper(BeeQueryWrapper warpper) {
        Object o = LambdaReflectionUtil.invoke(getMapper(warpper.getInstance()), "countByExample", warpper);
        if (o != null) {
            return NumberUtils.intValue(o.toString());
        }
        return Integer.valueOf(0);
    }


    @Override
    public List<T> findManyByWrapper(BeeQueryWrapper warpper) {
        Object o = LambdaReflectionUtil.invoke(getMapper(warpper.getInstance()), "selectByExample",  warpper);
        if (o != null) {
            return (List<T>) SqlProvider.getListFromListMap(o, warpper.getInstance());
        }
        return new ArrayList<>();
    }

    @Override
    public T findOneByWrapper(BeeQueryWrapper warpper) {
        warpper.onlyOne();
        List<T> list = this.findManyByWrapper(warpper);
        if(CollectionUtils.isEmpty(list)){
            return null;
        }
        return list.get(0);
    }

    @Override
    public BeePage<T> findManyByWrapperWithPage(BeeQueryWrapper warpper) {
        if (ObjectUtils.isNotEmpty(warpper.getPageSize()) && ObjectUtils.isNotEmpty(warpper.getPageStart())) {
            Page page;
            if(ObjectUtils.isNotEmpty(warpper.orderBy())) {
                page = PageMethod.startPage(warpper.getPageStart(), warpper.getPageSize(),warpper.orderBy());
            }else {
                page = PageMethod.startPage(warpper.getPageStart(), warpper.getPageSize());
            }
            LambdaReflectionUtil.invoke(getMapper(warpper.getInstance()), "selectByExample",  warpper);
            if (page != null) {
                return new BeePage<>(page,warpper);
            }
            return new BeePage(new ArrayList<T>());
        }else{
            return new BeePage(findManyByWrapper(warpper));
        }
    }


    @Override
    public Integer save(T t) {
        if(BeeDataSourceType.MONGODB_WRAP.getDriver().equals(TableUtils.getDriverName(t))){
            Map<String,Object> map = new HashMap<>();
            map.put("dto", t);
            return Integer.valueOf(TableUtils.getHandler(t).insert(map));
        }else {
            setIdValue(t);
            Object o = LambdaReflectionUtil.invoke(getMapper(t), "insertSelective",  t);
            if (o != null) {
                return NumberUtils.intValue(o.toString());
            }
            return Integer.valueOf(0);
        }
    }

    @Override
    public Integer insert(T t) {
        if(BeeDataSourceType.MONGODB_WRAP.getDriver().equals(TableUtils.getDriverName(t))){
            Map<String,Object> map = new HashMap<>();
            map.put("dto", t);
            return Integer.valueOf(TableUtils.getHandler(t).insert(map));
        }else {
            String keyName = "";
            for(Field field : t.getClass().getDeclaredFields()) {
                Id id = field.getAnnotation(Id.class);
                if(ObjectUtils.isNotEmpty(id)) {
                    keyName = field.getName();
                    break;
                }
            }
            Object o1 = ReflectionUtils.getFieldValue(getMapper(t), "h");
            if(ObjectUtils.isEmpty(ReflectionUtils.getField(o1.getClass(), "mapperInterface"))){
                Object target = AopTargetUtils.getTarget(getMapper(t));
                o1 =ReflectionUtils.getFieldValue(target, "h");
            }
            Object o2=ReflectionUtils.getFieldValue(o1, "sqlSession");
            SqlSessionFactory o3=(SqlSessionFactory) ReflectionUtils.getFieldValue(o2, "sqlSessionFactory");
            Configuration cf = o3.getConfiguration();
            MappedStatement ms =cf.getMappedStatement("com.mjk.common.core.jdbc.service.plus.mapper.WrapMapper.insertSelectivewithId");
            String[] keys = new String[] {keyName};
            ReflectionUtils.setFieldValue(ms, "keyColumns", keys);
            ReflectionUtils.setFieldValue(ms, "keyProperties",keys);
            Object o = LambdaReflectionUtil.invoke(getMapper(t), "insertSelectivewithId",  t);
            if (o != null) {
                return NumberUtils.intValue(o.toString());
            }
            return Integer.valueOf(0);
        }
    }

    @Override
    public Integer update(T t) {
        Object o = LambdaReflectionUtil.invoke(getMapper(t), "updateByPrimaryKeySelective",  t);
        if (o != null) {
            return NumberUtils.intValue(o.toString());
        }
        return Integer.valueOf(0);
    }

    @Override
    public Integer update(BeeQueryWrapper warpper) {
        if(BeeDataSourceType.MONGODB_WRAP.getDriver().equals(TableUtils.getDriverName(warpper.getInstance()))){
            Map<String,Object> map = new HashMap<>();
            map.put("warpper", warpper);
            map.put("dto", warpper.getInstance());
            return Integer.valueOf(TableUtils.getHandler(warpper.getInstance()).updateByExampleSelective(map));
        }else {
            Object o = LambdaReflectionUtil.invoke(getMapper(warpper.getInstance()), "updateByExampleSelective",  warpper);
            if (o != null) {
                return NumberUtils.intValue(o.toString());
            }
            return Integer.valueOf(0);
        }
    }

    @Override
    public Integer update(T entity, BeeQueryWrapper warpper) {
        if (BeeDataSourceType.MONGODB_WRAP.getDriver().equals(TableUtils.getDriverName(warpper.getInstance()))) {
            Map<String, Object> map = new HashMap<>();
            map.put("warpper", warpper);
            map.put("dto", warpper.getInstance());
            return Integer.valueOf(TableUtils.getHandler(warpper.getInstance()).updateByExampleSelective(map));
        } else {
            warpper.setInstance(entity);
            Object o = LambdaReflectionUtil.invoke(getMapper(warpper.getInstance()), "updateByExampleSelective", warpper);
            if (o != null) {
                return NumberUtils.intValue(o.toString());
            }
            return Integer.valueOf(0);
        }
    }


    @Override
    public Integer save(List<T> paramList) {
        if(CollectionUtils.isEmpty(paramList)){
            return 0;
        }
        for (T t : paramList) {
            setIdValue(t);
        }
        if (paramList.size() > defaultPageSize) {
            int result = 0;
            int totalPages = (int) Math.ceil((double) paramList.size() / defaultPageSize); // 总页数
            for(int currentPage = 1;currentPage <= totalPages;currentPage ++){
                int startIndex = (currentPage - 1) * defaultPageSize;
                int endIndex = Math.min(startIndex + defaultPageSize, paramList.size());
                Object o = LambdaReflectionUtil.invoke(getMapper(paramList.get(0)), "insertList", paramList.subList(startIndex, endIndex));
                result += NumberUtils.intValue(o.toString());
            }
            return result;
        }else {
            Object o = LambdaReflectionUtil.invoke(getMapper(paramList.get(0)), "insertList", paramList);
            return NumberUtils.intValue(o.toString());
        }
    }

    @Override
    public Integer update(List<T> paramList) {
        if (CollectionUtils.isEmpty(paramList)) {
            return 0;
        }
        if (paramList.size() > defaultPageSize) {
            int result = 0;
            int pageSize = defaultPageSize; // 每页显示的元素数量
            int totalElements = paramList.size(); // 列表中的总元素数量
            int totalPages = (int) Math.ceil((double) totalElements / pageSize); // 总页数
            for(int currentPage = 1;currentPage <= totalPages;currentPage ++){
                int startIndex = (currentPage - 1) * pageSize;
                int endIndex = Math.min(startIndex + pageSize, totalElements);
                Object o = LambdaReflectionUtil.invoke(getMapper(paramList.get(0)), "updateList", paramList.subList(startIndex, endIndex));
                result += NumberUtils.intValue(o.toString());
            }
            return result;
        }else {
            Object o = LambdaReflectionUtil.invoke(getMapper(paramList.get(0)), "updateList", paramList);
            return NumberUtils.intValue(o.toString());
        }
    }

    @Override
    public Integer saveOrUpdate(List<T> paramList) {
        if (CollectionUtils.isEmpty(paramList)) {
            return 0;
        }
        List<T> upList = paramList.stream().filter(item -> {
            List<String> keys = SqlProvider.getPramaryKeys(item);
            boolean isEmpty = false;
            for (String key : keys) {
                String primaryValues = SqlProvider.getPrimaryValues(item);
                if (ObjectUtils.isEmpty(primaryValues)) {
                    isEmpty = true;
                    break;
                }
            }
            return !isEmpty;
        }).collect(Collectors.toList());
        //差集
        List<T> insertList = paramList.stream().filter(item -> !upList.contains(item)).collect(Collectors.toList());
        int result = this.save(insertList);
        int re = this.update(upList);
        return result + re;
    }
    @Override
    public Integer delete(T t) {
        Object o = LambdaReflectionUtil.invoke(getMapper(t), "deleteByPrimaryKey",  t);
        if (o != null) {
            return NumberUtils.intValue(o.toString());
        }
        return Integer.valueOf(0);
    }

    @Override
    public Integer delete(BeeQueryWrapper warpper) {
        if(BeeDataSourceType.MONGODB_WRAP.getDriver().equals(TableUtils.getDriverName(warpper.getInstance()))){
            Map<String,Object> map = new HashMap<>();
            map.put("warp", warpper);
            return Integer.valueOf(TableUtils.getHandler(warpper.getInstance()).deleteByExample(map));
        }else {
            Object o = LambdaReflectionUtil.invoke(getMapper(warpper.getInstance()), "deleteByExample",warpper);
            if (o != null) {
                return NumberUtils.intValue(o.toString());
            }
            return Integer.valueOf(0);
        }
    }

    @Override
    public Integer delete(List<T> paramList) {
        Object o = LambdaReflectionUtil.invoke(getMapper(paramList.get(0)), "deleteList", paramList);
        if (o != null) {
            return NumberUtils.intValue(o.toString());
        }
        return Integer.valueOf(0);
    }

    @Override
    public List<Map<String, Object>> excuteSql(Map<String, Object> sql) {
        Object o = LambdaReflectionUtil.invoke(beeMapper, "excuteSql", sql);
        if(null!=o){
            return (List<Map<String, Object>>) o;
        }
        return new ArrayList<>();
    }

    @Override
    public void excuteSql(String paramString) {
        Map<String,Object> sql = new HashMap<>();
        sql.put("sql", paramString);
        excuteSql(sql);
    }

    /**
     * 设置主键值
     *
     * @param entity
     */
    private void setIdValue(BaseEntity entity) {
        List<Field> fileds = SqlProvider.getValidTableFiled(entity);
        for (Field f : fileds) {
            Id id = f.getAnnotation(Id.class);
            if (null != id) {
                Object val = BeanUtils.getFieldValue(entity, f.getName());
                if (ObjectUtils.isEmpty(val)) {
                    //获取主键值
                    String idvalue = id.type().getGenerator().getId(entity);
                    //设置值
                    BeanUtils.setFieldValue(entity, f.getName(), idvalue);
                }
            }
            //默认值
            DefaultValue defValue = f.getAnnotation(DefaultValue.class);
            if (null != defValue && ObjectUtils.isEmpty(ReflectionUtils.getFieldValue(this, f.getName()))) {
                BeanUtils.setFieldValue(this, f.getName(), defValue);
            }
        }
    }

    @Override
    public void doApplicationEvent(ContextRefreshedEvent event) {
        mappers = event.getApplicationContext().getBeansWithAnnotation(Mapper.class);
    }
}
